//
//  FYJScrollViewNestingSolution.m
//  CMSUIKit
//
//  Created by Michael on 2022/10/25.
//

#import "FYJScrollViewNestingSolution.h"
#import <FYJAspects/Aspects.h>
#import <FYJFoundation/FYJEXTScope.h>
#import <KVOController/KVOController.h>

@interface FYJScrollViewNestingSolution ()

@property (nonatomic, strong) UIScrollView<FYJScrollViewNestingSolutionProtocol> *mainScrollView;

@property (nonatomic, strong) UIScrollView *contentScrollView;

@property (nonatomic, strong) NSMutableArray<UIScrollView *> *scrollViews;

@property (nonatomic, assign) BOOL scrollToTopFlag;

@end

@implementation FYJScrollViewNestingSolution

- (instancetype)initWithMainScrollView:(UIScrollView<FYJScrollViewNestingSolutionProtocol> *)mainScrollView
{
    if (self = [super init]) {
        _mainScrollView = mainScrollView;
        _scrollViews = [NSMutableArray array];
        [self hookGestureRecognizerOfMainScrollView];
        [self observeContentOffsetOfMainScrollView];
    }
    return self;
}

- (void)addContentScrollView:(UIScrollView *)contentScrollView
{
    if (!contentScrollView) {
        return;
    }
    _contentScrollView = contentScrollView;
    if (![self.scrollViews containsObject:self.contentScrollView]) {
        [self observeContentOffsetOfContentScrollView];
        [self.scrollViews addObject:self.contentScrollView];
    }
}

- (void)scrollToTopOfMainScrollView
{
    self.scrollToTopFlag = YES;
    [self.contentScrollView setContentOffset:CGPointZero animated:NO];
    [self.mainScrollView setContentOffset:CGPointZero animated:NO];
    self.scrollToTopFlag = NO;
}

- (void)hookGestureRecognizerOfMainScrollView
{
    @weakify(self);
    [self.mainScrollView aspect_hookSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, UIGestureRecognizer *gestureRecognizer, UIGestureRecognizer *otherGestureRecognizer) {
        @strongify(self);
        if ([gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class] &&
            [otherGestureRecognizer isKindOfClass:UIPanGestureRecognizer.class] &&
            [gestureRecognizer.view isEqual:self.mainScrollView] &&
            [otherGestureRecognizer.view isEqual:self.contentScrollView]) {
            BOOL returnValue = YES;
            [aspectInfo.originalInvocation setReturnValue:&returnValue];
        }
    } error:nil];
}

- (void)observeContentOffsetOfMainScrollView
{
    static BOOL manualSetOffset = NO;
    @weakify(self);
    [[self KVOController] observe:self.mainScrollView keyPath:@"contentOffset"  options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        @strongify(self);
        if (self.scrollToTopFlag) {
            return;
        }
        if (manualSetOffset) {
            manualSetOffset = NO;
            return;
        }
        if (self.contentScrollView && self.contentScrollView.contentOffset.y > [self minContentOffsetYInListScrollView:self.contentScrollView]) {
            manualSetOffset = YES;
            self.mainScrollView.contentOffset = CGPointMake(0, self.maxContentOffsetYOfMainScrollView);
        }

        if (self.mainScrollView.contentOffset.y < self.maxContentOffsetYOfMainScrollView) {
            self.contentScrollView.contentOffset = CGPointMake(self.mainScrollView.contentOffset.x, [self minContentOffsetYInListScrollView:self.mainScrollView]);
        }

        if (self.mainScrollView.contentOffset.y > self.maxContentOffsetYOfMainScrollView &&
            self.contentScrollView.contentOffset.y == [self minContentOffsetYInListScrollView:self.contentScrollView]) {
            manualSetOffset = YES;
            self.mainScrollView.contentOffset = CGPointMake(0, self.maxContentOffsetYOfMainScrollView);
        }
    }];
}

- (void)observeContentOffsetOfContentScrollView
{
    static BOOL manualSetOffset = NO;
    @weakify(self);
    [[self KVOController] observe:self.contentScrollView keyPath:@"contentOffset"  options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        @strongify(self);
        if (self.scrollToTopFlag) {
            return;
        }
        if (self.mainScrollView.contentOffset.y < self.maxContentOffsetYOfMainScrollView) {
            if (manualSetOffset) {
                manualSetOffset = NO;
                return;
            }
            manualSetOffset = YES;
            self.contentScrollView.contentOffset = CGPointMake(self.contentScrollView.contentOffset.x, [self minContentOffsetYInListScrollView:self.contentScrollView]);
        } else {
            self.mainScrollView.contentOffset = CGPointMake(0, self.maxContentOffsetYOfMainScrollView);
        }
    }];
}

- (CGFloat)minContentOffsetYInListScrollView:(UIScrollView *)scrollView
{
    if (@available(iOS 11.0, *)) {
        return -scrollView.adjustedContentInset.top;
    }
    return -scrollView.contentInset.top;
}

@end
